#include "defaults.h"
#include "nurbs.h"
#include "utils.h"
#include "testoptimizer.h"
#include "optimizer.h"
#include "nurbspatchenergies.h"
#include "triangleenergies.h"
#include "primitivemanager.h"

extern PrimitiveManager* primitiveManager;

void testNurbsPatchMehlumTarrouEnergy() {
	Surface surface = PeriodicCylindricalSurface(0.5, 1.0, 10, 20, 3, 3);//create tensor product B-spline surface
// 	Surface surface = OpenSurface("file.txt"); //or open from file alternatively

	NurbsSurfaceMesh* nsm = GenerateSurface(surface, false); //convert into FEM representation (conversion is exact) 

	PRINTVAR(nsm->SizeNodes());
	nsm->AddNodeAttribute<size_t>("mark", 0); //all vertices are marked with 0, i.e. they are not fixed

	std::vector<int> row;
	row.push_back(0); //first row
	row.push_back(1); //second row
	row.push_back(-2); //second last row
	row.push_back(-1); //last row

	std::vector<Vec3n> P;
	for(size_t i = 0; i < row.size(); i++) {
		for(size_t j = 0; j < surface.Size1() - surface.GetDegreeV(); j++) {
			const Vec4n& x = surface(row[i], j);
			P.push_back(math::V3n(x[0], x[1], x[2]));
		}
	}

	//find nodes which correspond to first and last rows of surface
	std::vector<Node*> node = FindNodes<Vec3n>(nsm, P, "position");

	for(size_t i = 0; i < node.size(); i++) {
		Vec3n& P = node[i]->Get<Vec3n>("position");
		if(P[2] < 0.01)
			P = math::V3n(1.5 * P[0], 1.5 * P[1], P[2]);
		else if(P[2] > 0.99)
			P = math::V3n(1.25 * P[0], 1.25 * P[1], P[2]);

		node[i]->Get<size_t>("mark") = 1;//fix vertex

		visualDebugger.AddSphere(P, math::V3n(1.0, 0.0, 0.0), 0.01);
	}

	Optimizer::Config config;
	config.Init("config.xml");//configuration file for optimizer

	Optimizer::NurbsPatchFunctional* functional = new Optimizer::MehlumTarrouEnergyFunctional();
// 	Optimizer::NurbsPatchFunctional* functional = new Optimizer::WillmoreEnergyFunctional();

	Optimizer::PrimitiveEvaluator* primitiveEval = new Optimizer::NurbsPatchEvaluator(functional);

	Optimizer::Evaluator* eval = new Optimizer::NurbsSurfaceEvaluator(nsm, primitiveEval);

	Optimizer::MinimizeMultiScale(nsm, eval, config);

	primitiveManager->AddPrimitive(nsm, "surface");
}

void testEdgeWillmoreEnergy() {
	STriangleMesh* tm = OpenTriangleMesh(GetPath("bunny10k.txt"));
	Optimizer::PrimitiveEvaluator* primitiveEval = new Optimizer::BendingEnergyEdgeEvaluator();//Willmore Energy
	Optimizer::Evaluator* eval = new Optimizer::EdgeEvaluator(tm, primitiveEval);
	
	Optimizer::Config config;
	config.Init("config.xml");

	Optimizer::MinimizeMultiScale(tm, eval, config);

	primitiveManager->AddPrimitive(tm, "trianglemesh");
}

void testOptimizer(const std::vector<std::string>& argument) {
	testNurbsPatchMehlumTarrouEnergy();
// 	testEdgeWillmoreEnergy();
}
